<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0,viewport-fit=cover"><title>全栈开发(RESTful和测试) | 静待花开</title><meta name="author" content="brad 随风起舞"><meta name="copyright" content="brad 随风起舞"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="这篇博客,主要就是使用restTemplate去消费服务,然后使用spring的测试框架测试REST服务的CURD功能.   使用RestTemplate构建一个REST客户端RestTemplate     org.springframework.web.client.RestTemplate在spring中被用于构建REST客户端去消费REST服务的.在Spring的中心类是Rest">
<meta property="og:type" content="article">
<meta property="og:title" content="全栈开发(RESTful和测试)">
<meta property="og:url" content="https://zhangbo4881820.gitee.io/myblog/posts/21667/index.html">
<meta property="og:site_name" content="静待花开">
<meta property="og:description" content="这篇博客,主要就是使用restTemplate去消费服务,然后使用spring的测试框架测试REST服务的CURD功能.   使用RestTemplate构建一个REST客户端RestTemplate     org.springframework.web.client.RestTemplate在spring中被用于构建REST客户端去消费REST服务的.在Spring的中心类是Rest">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png">
<meta property="article:published_time" content="2019-11-06T01:09:06.000Z">
<meta property="article:modified_time" content="2023-09-07T09:44:01.887Z">
<meta property="article:author" content="brad 随风起舞">
<meta property="article:tag" content="实战">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png"><link rel="shortcut icon" href="/myblog/img/favicon.png"><link rel="canonical" href="https://zhangbo4881820.gitee.io/myblog/posts/21667/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/myblog/css/index.css"><link rel="stylesheet" href="/myblog/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="/myblog/css/fancybox.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = {
  root: '/myblog/',
  algolia: undefined,
  localSearch: {"path":"/myblog/search.xml","preload":false,"top_n_per_article":1,"unescape":false,"languages":{"hits_empty":"找不到您查询的内容：${query}","hits_stats":"共找到 ${hits} 篇文章"}},
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  dateSuffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false,
  percent: {
    toc: true,
    rightside: false,
  },
  autoDarkmode: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: '全栈开发(RESTful和测试)',
  isPost: true,
  isHome: false,
  isHighlightShrink: true,
  isToc: true,
  postUpdate: '2023-09-07 17:44:01'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
    win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = url
      if (id) link.id = id
      link.onerror = reject
      link.onload = link.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        link.onload = link.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(link)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 7.0.0-rc2"><link rel="alternate" href="/myblog/atom.xml" title="静待花开" type="application/atom+xml">
</head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/myblog/archives/"><div class="headline">文章</div><div class="length-num">57</div></a><a href="/myblog/tags/"><div class="headline">标签</div><div class="length-num">20</div></a><a href="/myblog/categories/"><div class="headline">分类</div><div class="length-num">25</div></a></div><hr class="custom-hr"/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/myblog/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 分享</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/myblog/Gallery/"><i class="fa-fw fas fa-images"></i><span> 照片</span></a></li><li><a class="site-page child" href="/myblog/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/myblog/movies/"><i class="fa-fw fas fa-video"></i><span> 视频</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/myblog/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="not-top-img" id="page-header"><nav id="nav"><span id="blog-info"><a href="/myblog/" title="静待花开"><span class="site-name">静待花开</span></a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search" href="javascript:void(0);"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/myblog/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 分享</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/myblog/Gallery/"><i class="fa-fw fas fa-images"></i><span> 照片</span></a></li><li><a class="site-page child" href="/myblog/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/myblog/movies/"><i class="fa-fw fas fa-video"></i><span> 视频</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/myblog/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav></header><main class="layout" id="content-inner"><div id="post"><div id="post-info"><h1 class="post-title">全栈开发(RESTful和测试)</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2019-11-06T01:09:06.000Z" title="发表于 2019-11-06 09:09:06">2019-11-06</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-09-07T09:44:01.887Z" title="更新于 2023-09-07 17:44:01">2023-09-07</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/myblog/categories/%E5%AE%9E%E6%88%98/">实战</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="全栈开发(RESTful和测试)"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div><article class="post-content" id="article-container"><font color="F92472" size="8" face="宋体">
    这篇博客,主要就是使用restTemplate去消费服务,然后使用spring的测试框架测试REST服务的CURD功能.
</font>

<h1 id="使用RestTemplate构建一个REST客户端"><a href="#使用RestTemplate构建一个REST客户端" class="headerlink" title="使用RestTemplate构建一个REST客户端"></a>使用RestTemplate构建一个REST客户端</h1><h2 id="RestTemplate"><a href="#RestTemplate" class="headerlink" title="RestTemplate"></a>RestTemplate</h2><p>
    org.springframework.web.client.RestTemplate在spring中被用于构建REST客户端去消费REST服务的.在Spring的中心类是RestTemplate,在客户端消费REST服务时它可以同步调用.它是线程安全的.它是基于模板方法设计模式设计的.
</p>
<p>
    RestTemplate是Spring-web.jar文件的一部分.所以要使用它需要增加spring-web的依赖.如下面代码例子
</p>

<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&lt;dependencies&gt; </span><br><span class="line">&lt;dependency&gt; </span><br><span class="line">&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; </span><br><span class="line">&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt; </span><br><span class="line">&lt;/dependency&gt; </span><br><span class="line">&lt;/dependencies&gt;</span><br></pre></td></tr></table></figure>
<h2 id="RestTemplate-方法"><a href="#RestTemplate-方法" class="headerlink" title="RestTemplate 方法"></a>RestTemplate 方法</h2><table>
    <tr>
        <th>HTTP Method</th>
        <th>RestTemplate Method</th>
    </tr>
    <tr>
        <td rowspan="2">GET</td>
        <td>getForObject(java.lang.String, java.lang.Class<T>, java.lang.Object...)</td>
    </tr>
    <tr>
        <td><font color="Hotpink">getForEntity(java.lang.String, java.lang.Class<T>, java.lang.Object...)</font></td>
    </tr>
    <tr>
        <td rowspan="3">POST</td>
        <td><font color="Hotpink">postForLocation(java.lang.String, java.lang.Object, java.lang.Object...)</font></td>
    </tr>
    <tr>
        <td><font color="Hotpink">postForObject(java.lang.String, java.lang.Object, java.lang.Class<T>, java.lang.Object...)</font></td>
    </tr>
    <tr>
        <td>postForEntity(postForObject(java.lang.String, java.lang.Object, java. lang.Class<T>, java.lang.Object...)</td>
    </tr>
    <tr>
        <td>PUT</td>
        <td><font color="Hotpink">put(java.lang.String, java.lang.Object, java.lang.Object...)</font></td>
    </tr>
    <tr>
        <td>DELETE</td>
        <td><font color="Hotpink">delete(java.lang.String, java.lang.Object...)</font></td>
    </tr>
    <tr>
        <td>HEAD</td>
        <td><font color="Hotpink">headForHeaders(java.lang.String, java.lang.Object...)</font></td>
    </tr>
    <tr>
        <td>OPTIONS</td>
        <td><font color="Hotpink">optionsForAllow(java.lang.String, java.lang.Object...)</font></td>
    </tr>
    <tr>
        <td>ANY</td>
        <td><font color="Hotpink">exchange(java.lang.String, org.springframework.http.HttpMethod, org. springframework.http.HttpEntity<?>, java.lang.Class<T>, java.lang. Object...)</font></td>
    </tr>
</table>

<h1 id="带参数的GET请求去获取用户"><a href="#带参数的GET请求去获取用户" class="headerlink" title="带参数的GET请求去获取用户"></a>带参数的GET请求去获取用户</h1><p>getForObject和getForEntity都可以发GET请求,我们使用getForObject方法来发get请求.RestTemplate提供了getForObject三个重载的版本.如下所示:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">@Override </span><br><span class="line">public &lt;T&gt; T getForObject(String url, Class&lt;T&gt; responseType, Object... </span><br><span class="line">	uriVariables) throws RestClientException &#123; </span><br><span class="line">		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor&lt;T&gt; responseExtractor = </span><br><span class="line">		new HttpMessageConverterExtractor&lt;T&gt;(responseType, </span><br><span class="line">			getMessageConverters(), logger); </span><br><span class="line">			return execute(url, HttpMethod.GET, requestCallback, </span><br><span class="line">				responseExtractor, uriVariables); </span><br><span class="line">				&#125; </span><br><span class="line">@Override </span><br><span class="line">public &lt;T&gt; T getForObject(String url, Class&lt;T&gt; responseType, Map&lt;String, ?&gt; </span><br><span class="line">	uriVariables) throws RestClientException &#123; </span><br><span class="line">		RequestCallback requestCallback = </span><br><span class="line">		acceptHeaderRequestCallback(responseType);</span><br><span class="line">		HttpMessageConverterExtractor&lt;T&gt; responseExtractor = </span><br><span class="line">			new HttpMessageConverterExtractor&lt;T&gt;(responseType, </span><br><span class="line">			getMessageConverters(), logger); </span><br><span class="line">		return execute(url, HttpMethod.GET, requestCallback, </span><br><span class="line">			responseExtractor, uriVariables); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Override </span><br><span class="line">public &lt;T&gt; T getForObject(URI url, Class&lt;T&gt; responseType) throws </span><br><span class="line">RestClientException &#123; </span><br><span class="line">	RequestCallback requestCallback = </span><br><span class="line">	acceptHeaderRequestCallback(responseType); </span><br><span class="line">	HttpMessageConverterExtractor&lt;T&gt; responseExtractor = </span><br><span class="line">		new HttpMessageConverterExtractor&lt;T&gt;(responseType, </span><br><span class="line">		getMessageConverters(), logger); </span><br><span class="line">		return execute(url, HttpMethod.GET, requestCallback, </span><br><span class="line">			responseExtractor); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>前两个重载方法包含一个以String形式的URI模板,一个返回值类型,一个URI变量作为方法的参数.最后那个重载方法包含两个参数,一个完整形式的URI和一个返回值类型.</p>
<p>接下来创建客户端类.</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">/**</span><br><span class="line"> * create by zhangbo on 2019/11/6 0006</span><br><span class="line"> */</span><br><span class="line">public class UserRegistrationClient &#123;</span><br><span class="line"></span><br><span class="line">    private static RestTemplate restTemplate = new RestTemplate();</span><br><span class="line">    private static final String BASE_URL = &quot;http://localhost:8080/api/user/&quot;;</span><br><span class="line"></span><br><span class="line">    public UserDTO getUserById(final Long id) &#123;</span><br><span class="line"></span><br><span class="line">        return restTemplate.getForObject(BASE_URL+&quot;/&#123;id&#125;&quot;,UserDTO.class,id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public UserDTO[] getAllUsers() &#123;</span><br><span class="line"></span><br><span class="line">        return restTemplate.getForObject(BASE_URL,UserDTO[].class);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        UserRegistrationClient userRegistrationClient = new UserRegistrationClient();</span><br><span class="line">        UserDTO user = userRegistrationClient.getUserById(2L);</span><br><span class="line">        System.out.println(&quot;USER-ID&quot;+user.getId()+&quot;USER-NAME&quot;+user.getName());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="带JSON数据的POST访问创建用户"><a href="#带JSON数据的POST访问创建用户" class="headerlink" title="带JSON数据的POST访问创建用户"></a>带JSON数据的POST访问创建用户</h1><p>postForLocation,postForObject,postForEntity,这三个方法都可以发送post请求.我们使用postForObject方法来发送post请求.对于postForObject也有三个重载版本</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">@Override public &lt;T&gt; T postForObject(String url, Object request,Class&lt;T&gt; responseType, </span><br><span class="line">	Object... uriVariables) throws RestClientException &#123; </span><br><span class="line">		RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor&lt;T&gt; responseExtractor = </span><br><span class="line">			new HttpMessageConverterExtractor&lt;T&gt;( responseType, getMessageConverters(), logger);</span><br><span class="line">		return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Override </span><br><span class="line">public &lt;T&gt; T postForObject(String url, Object request, Class&lt;T&gt;responseType,</span><br><span class="line">	Map&lt;S tring,?&gt;uriVariables)throws RestClientException&#123; </span><br><span class="line">		RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor&lt;T&gt; responseExtractor = </span><br><span class="line">			new HttpMessageConverterExtractor&lt;T&gt;( responseType, getMessageConverters(), logger); </span><br><span class="line">		return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Override </span><br><span class="line">public &lt;T&gt; T postForObject(URI url, Object request, Class&lt;T&gt; responseType) </span><br><span class="line">throws RestClientException &#123; </span><br><span class="line">	RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor&lt;T&gt; responseExtractor = </span><br><span class="line">	new HttpMessageConverterExtractor&lt;T&gt;( responseType, getMessageConverters()); </span><br><span class="line">	return execute(url, HttpMethod.POST, requestCallback, responseExtractor); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>前两个重在方法包含一个String形式的URI模板,一个请求对象,一个返回值类型和URI参数变量作为方法参数.第三个重载方法带有三个参数:完整形式的URI,请求对象,返回值类型.在上面UserRegistrationClient类里增加方法</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">public UserDTO createUser(final UserDTO user) &#123;</span><br><span class="line">       return restTemplate.postForObject(BASE_URL, user, UserDTO.class);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>主函数增加如下代码:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">UserDTO userDTO = new UserDTO();</span><br><span class="line">userDTO.setName(&quot;张小龙&quot;);</span><br><span class="line">userDTO.setAddress(&quot;北京&quot;);</span><br><span class="line">userDTO.setEmail(&quot;545454@qq.com&quot;);</span><br><span class="line">UserDTO newUser = userRegistrationClient.createUser(userDTO);</span><br><span class="line">System.out.println(newUser.getId());</span><br></pre></td></tr></table></figure>

<h1 id="带参数的put请求更改用户"><a href="#带参数的put请求更改用户" class="headerlink" title="带参数的put请求更改用户"></a>带参数的put请求更改用户</h1><p>同样有三种重载方法</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">@Override </span><br><span class="line">public void put(String url, Object request, Object... </span><br><span class="line">	uriVariables) throws RestClientException &#123; </span><br><span class="line">		RequestCallback requestCallback = </span><br><span class="line">		httpEntityCallback(request); </span><br><span class="line">		execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Override </span><br><span class="line">public void put(String url, Object request, </span><br><span class="line">	Map&lt;String, ?&gt; uriVariables) throws RestClientException &#123; </span><br><span class="line">		RequestCallback requestCallback = httpEntityCallback(request); </span><br><span class="line">		execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Override </span><br><span class="line">public void put(URI url, Object request) throws RestClientException &#123; </span><br><span class="line">	RequestCallback requestCallback = httpEntityCallback(request); </span><br><span class="line">	execute(url, HttpMethod.PUT, requestCallback, null); </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>前两个重载方法包含一个String形式的URI模板,一个请求对象,URI参数变量,作为方法参数.第三个重载方法带有两个参数:一个完整形式的URI,和一个请求对象.没有返回值.代码如下</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">public void updateUser(final Long userId, final UserDTO user) &#123;</span><br><span class="line">    restTemplate.put(BASE_URL + &quot;/&#123;id&#125;&quot;, user, userId);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="带参数的delete访问删除用户"><a href="#带参数的delete访问删除用户" class="headerlink" title="带参数的delete访问删除用户"></a>带参数的delete访问删除用户</h1><p>也有三个重载方法.</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">@Override </span><br><span class="line">public void delete(String url, Object... uriVariables) throws RestClientException &#123; </span><br><span class="line">	execute(url, HttpMethod.DELETE, null, null, uriVariables); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Override </span><br><span class="line">public void delete(String url, Map&lt;String, ?&gt; uriVariables) throws RestClientException &#123; </span><br><span class="line">	execute(url, HttpMethod.DELETE, null, null, uriVariables); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Override </span><br><span class="line">public void delete(URI url) throws RestClientException &#123; </span><br><span class="line">	execute(url, HttpMethod.DELETE, null, null); </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>前两个重载方法包含一个String形式的URI模板,URI参数变量作为方法参数.第三个重载方法带有一个参数:一个完整形式的URI,没有返回值.代码如下</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">public void deleteUser(final Long userId) &#123;</span><br><span class="line">    restTemplate.delete(BASE_URL + &quot;/&#123;id&#125;&quot;, userId);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="RestTemplate-的-Exchange-API"><a href="#RestTemplate-的-Exchange-API" class="headerlink" title="RestTemplate 的 Exchange API"></a>RestTemplate 的 Exchange API</h1><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">public ResponseEntity&lt;UserDTO&gt; getUserByIdUsingExchangeAPI(final Long userId) &#123;</span><br><span class="line">       HttpEntity&lt;UserDTO&gt; httpEntity = new HttpEntity&lt;UserDTO&gt;(new UserDTO());</span><br><span class="line">       return restTemplate.exchange(USER_REGISTRATION_BASE_URL + &quot;/&#123;id&#125;&quot;, HttpMethod.GET, httpEntity, UserDTO.class, userId);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<h1 id="RestTemplate的基本认证"><a href="#RestTemplate的基本认证" class="headerlink" title="RestTemplate的基本认证"></a>RestTemplate的基本认证</h1><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">public void deleteUserById(Long userId) &#123;</span><br><span class="line">       String userCredential = securityUserName + &quot;:&quot; + securityUserPassword;</span><br><span class="line">       byte[] base64UserCredentialData = Base64.encodeBase64(userCredential.getBytes());</span><br><span class="line">       HttpHeaders authenticationHeaders = new HttpHeaders();</span><br><span class="line">       authenticationHeaders.set(&quot;Authorization&quot;, &quot;Basic &quot; + new String(base64UserCredentialData));</span><br><span class="line">       HttpEntity&lt;Void&gt; httpEntity = new HttpEntity&lt;Void&gt;(authenticationHeaders);</span><br><span class="line">       restTemplate.exchange(USER_REGISTRATION_BASE_URL + &quot;/&#123;id&#125;&quot;, HttpMethod.DELETE, httpEntity, Void.class, userId);</span><br><span class="line"></span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<h1 id="使用Spring的测试框架测试RESTful服务"><a href="#使用Spring的测试框架测试RESTful服务" class="headerlink" title="使用Spring的测试框架测试RESTful服务"></a>使用Spring的测试框架测试RESTful服务</h1><p>测试是软件开发生命周期中至关重要的部分。它是确保软件质量和性能的过程，否则就不能完成软件开发。</p>
<p>单元测试意味着独立或分别测试应用程序的每个组件，而集成测试则有助于确保系统中的多个组件运行良好。</p>
<p>在单独的源文件夹（例如<code>src/test/java</code>）或单独的项目中创建单元测试是一个好习惯。</p>
<p>您可以自动或手动进行测试.自动化测试的好处是,它可以在软件开发过程的不同阶段连续不断地运行,强烈建议您遵循敏捷开发过程<br>由于Spring Framework本质上是敏捷的，因此它支持这种过程</p>
<p>接下来使用JUnit测试</p>
<h2 id="Testing-Using-JUnit4"><a href="#Testing-Using-JUnit4" class="headerlink" title="Testing Using JUnit4"></a>Testing Using JUnit4</h2><p>(<a href="http://junit.org/">http://junit.org/</a>) 一个非常流行单元测试框架.提供@test注解去标注需要测试的方法.测试方法所在的类需要被@org.junit.Test 标注.还有其他的注释方法和assert方法去执行单元测试.</p>
<h3 id="注解清单"><a href="#注解清单" class="headerlink" title="注解清单"></a>注解清单</h3><table>
    <tr>
        <th>注解</th>
        <th>包</th>
        <th>描述</th>
    </tr>
    <tr>
        <td>@Test</td>
        <td>org.junit.Test</td>
        <td>使用@Test注释对public void方法进行注释,以标识和运行测试用例</td>
    </tr>
    <tr>
        <td>@Before</td>
        <td>org.junit.Before</td>
        <td>使用@Before注释对public void方法进行注释,被标识后的方法需要测试类中其他测试方法之前执行.这个方法可以用于设置环境变量
        </td>
    </tr>
    <tr>
        <td>@After</td>
        <td>org.junit.After</td>
        <td>使用@After注释对public void方法进行注释,被标识后的方法需要测试类中其他测试方法之后执行.这个方法可以用于释放资源</td>
    </tr>
    <tr>
        <td>@BeforeClass</td>
        <td>org.junit.BeforeClass</td>
        <td>使用@BeforeClass注释对public static void方法进行注释,被标识后的方法在类中其他所有测试方法执行之前执行且只执行一次
        </td>
    </tr>
    <tr>
        <td>@AfterClass</td>
        <td>org.junit.AfterClass</td>
        <td>使用@AfterClass注释对public static 
        void方法进行注释,被标识后的方法在类中其他所有测试方法执行之后执行且只执行一次.可用于清理工作</td>
    </tr>
    <tr>
        <td>@Ignore</td>
        <td>org.junit.Ignore</td>
        <td>被@Ignore注释的方法,将不会执行</td>
    </tr>
</table>

<h3 id="Assert方法"><a href="#Assert方法" class="headerlink" title="Assert方法"></a>Assert方法</h3><p>JUnit框架提供org.junit.Assert,这个类有很多有用的方法去些测试单元,在特定条件下测试.这些方法通过比较期待值和实际值去验证测试结果.</p>
<h3 id="Assert方法清单"><a href="#Assert方法清单" class="headerlink" title="Assert方法清单"></a>Assert方法清单</h3><table>
    <tr>
        <th>Assert方法</th>
        <th>描述</th>
    </tr>
    <tr>
        <td>assertTrue(boolean expected, boolean actual)</td>
        <td>此方法检查布尔条件是否为true</td>
    </tr>
    <tr>
        <td>assertFalse(boolean condition)</td>
        <td>此方法检查布尔条件是否为false
        </td>
    </tr>
    <tr>
        <td>assertEquals(boolean expected, expected, actual)</td>
        <td>此方法使用equals()方法比较两个对象是否相等</td>
    </tr>
    <tr>
        <td>assertEquals(boolean expected, expected, actual, tolerance)</td>
        <td>此方法比较浮点数或双精度值，并且精度定义的小数位数必须相同
        </td>
    </tr>
    <tr>
        <td>assertNull(Object object)</td>
        <td>测试一个给定的对象是null的</td>
    </tr>
    <tr>
        <td>assertNotNull(Object object)</td>
        <td>测试一个给定的对象不是null的</td>
    </tr>
    <tr>
        <td>assertSame(Object object1,Object object2)</td>
        <td>测试两个对象是否引用相同的对象</td>
    </tr>
    <tr>
        <td>assertNotSame(Object object1,Object object2)</td>
        <td>测试两个对象是否不是引用相同的对象</td>
    </tr>
</table>

<h2 id="代码实现Junit测试"><a href="#代码实现Junit测试" class="headerlink" title="代码实现Junit测试"></a>代码实现Junit测试</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">package com.zhang.myproject.springTest;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * create by zhangbo on 2019/11/6 0006</span><br><span class="line"> */</span><br><span class="line">public class SimpleCalculator &#123;</span><br><span class="line"></span><br><span class="line">    public long addOperation(int x,int y) &#123;</span><br><span class="line">        return x + y;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * create by zhangbo on 2019/11/6 0006</span><br><span class="line"> */</span><br><span class="line">public class SimpleCalculatorTests &#123;</span><br><span class="line"></span><br><span class="line">    private SimpleCalculator simpleCalculator;</span><br><span class="line"></span><br><span class="line">    @Before</span><br><span class="line">    public void setup() &#123;</span><br><span class="line">        simpleCalculator = new SimpleCalculator();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Test</span><br><span class="line">    public void verifyAdd() &#123;</span><br><span class="line">        long sum = simpleCalculator.addOperation(2, 1);</span><br><span class="line">        Assert.assertEquals(3, sum);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @After</span><br><span class="line">    public void teardown() &#123;</span><br><span class="line">        simpleCalculator = null;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h1 id="Agile-Software-Testing-敏捷软件测试"><a href="#Agile-Software-Testing-敏捷软件测试" class="headerlink" title="Agile Software Testing(敏捷软件测试)"></a>Agile Software Testing(敏捷软件测试)</h1><p>在软件开发世界中，敏捷是指项目管理方法，其中团队通过在应用程序中新模块的开发过程中专注于协作，灵活性，简单性和响应性的原则来开展工作。当使用敏捷软件方法时，团队将在称为冲刺的较小部门中交付项目或分配的作品，而跨部门团队（包括利益相关者）之间将进行持续协作。这种特定的方法本质上是渐进的和迭代的，着重于有效交付已分配的作品。</p>
<p>敏捷软件测试是指在敏捷工作流程的上下文中测试软件是否存在任何错误和性能问题的实践。它包括单元测试以及集成测试</p>
<h2 id="单元测试"><a href="#单元测试" class="headerlink" title="单元测试"></a>单元测试</h2><p>单元测试是指测试代码中的各个功能&#x2F;方法。单元测试由软件开发人员编写，用于测试其代码中的基本功能并防止错误。</p>
<p>短语“测试中的类”指的是这样的概念：只要编写任何单元测试用例，就针对要测试的类创建一个测试类，并且必须为其编写单元测试用例</p>
<h3 id="单元测试用Mocking对象处理依赖类"><a href="#单元测试用Mocking对象处理依赖类" class="headerlink" title="单元测试用Mocking对象处理依赖类"></a>单元测试用Mocking对象处理依赖类</h3><h4 id="Mockito框架"><a href="#Mockito框架" class="headerlink" title="Mockito框架"></a>Mockito框架</h4><p>使用Mockito需要对应的包和JUnit包,它支持属性级别的注解如下:</p>
<ol>
<li><p><code>@Mock</code>: 这个注解被用作为一个被注解的字段创建一个mock对象</p>
</li>
<li><p><code>@Spy</code>: 此注释会为其注释的真实对象字段创建一个间谍，当监视或触碰到特定方法时，可以将其称为部分模拟。</p>
</li>
<li><p><code>@RunWith(MockitoJunitRunner.class)</code>:使用@Mock批注和@Spy批注创建模拟对象需要将<br> @RunWith(MockitoJUnitRunner.class)批注应用于测试类。<br> 当MockitoJUnitRunner执行单元测试时,所有带注释的字段都将创建模拟对象和间谍对象。</p>
</li>
</ol>
<p>需要了解更多请访问:(<a href="http://mockito.org/">http://mockito.org/</a>)</p>
<h1 id="在Spring-boot应用中测试"><a href="#在Spring-boot应用中测试" class="headerlink" title="在Spring boot应用中测试"></a>在Spring boot应用中测试</h1><p>pring Framework中的spring-test模块允许开发人员通过提供丰富的批注，实用程序类和模拟对象来执行单元测试和集成测试。<br>让我们通过使用所需的依赖关系更新Maven pom.xml文件来设置环境</p>
<h2 id="Maven依赖"><a href="#Maven依赖" class="headerlink" title="Maven依赖"></a>Maven依赖</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&lt;dependency&gt; </span><br><span class="line">&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; </span><br><span class="line">&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt; </span><br><span class="line">&lt;scope&gt;test&lt;/scope&gt; </span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure>
<p>这个包带有JUnit,AssertJ,Mockito,Spring Test和Spring boot Test</p>
<h2 id="实现代码"><a href="#实现代码" class="headerlink" title="实现代码"></a>实现代码</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">@SpringBootTest(classes = HellospringbootApplication.class,</span><br><span class="line">        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)</span><br><span class="line">@RunWith(SpringRunner.class)</span><br><span class="line">class HellospringbootApplicationTests &#123;</span><br><span class="line"></span><br><span class="line">    @Before</span><br><span class="line">    public void setup() &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Test</span><br><span class="line">    public void testFunction() &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @After</span><br><span class="line">    public void teardown() &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>注解讲解</strong></p>
<ul>
<li><p><code>@RunWith(SpringRunner.class)</code>: 这个注解通知Junit去使用SpringJUnit4ClassRunner类去运行测试用例.SpringRunner<br>继承SpringJUnit4ClassRunner这个类.@RunWith这注解是Junit注解.它在被@Runwith标注的类中或者他的继承类注解标注的<br>类中执行测试.<code>这意味着通过JUnit框架中构建的API注解的测试类将不会执行</code>相反的在SpringApplicationContext环境内他会使用SpringJUnit4ClassRunner去执行测试用例</p>
</li>
<li><p><code>@SpringBootTest</code>: Spring Boot提供@SpringBootTest注释以在Spring Boot的支持下进行引导;<br>当您需要Spring Boot功能时,它可以替代标准的Spring Test @ContextConfiguration批注。<br>@ContextConfiguration批注用于测试测试类的应用程序上下文。它在测试或测试套件的整个过程中缓存ApplicationContext并<br>将其放置在静态内存中，并且由于ApplicationContext存储在静态内存中，因此整个测试在同一JVM中执行。</p>
</li>
<li><p><code>WebEnvironment</code>: 这是@SpringbootTest为测试配置的Web环境.它让测试单元具有模拟的servlet环境或运行在<br>DEFINED_PORT或RANDOM_PORT端口的真实HTTP服务环境</p>
</li>
<li><p><code>classes</code> 可以使用这个属性去加载指定的配置.默认的它会去搜索@SpringBootApplication标注的类<br>然后加载@configuration配置</p>
</li>
</ul>
<h2 id="测试REST-API"><a href="#测试REST-API" class="headerlink" title="测试REST API"></a>测试REST API</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">import org.junit.jupiter.api.*;</span><br><span class="line">import org.junit.runner.RunWith;</span><br><span class="line">import org.mockito.Mock;</span><br><span class="line">import org.mockito.Spy;</span><br><span class="line">import org.springframework.boot.test.context.SpringBootTest;</span><br><span class="line">import org.springframework.http.HttpStatus;</span><br><span class="line">import org.springframework.http.ResponseEntity;</span><br><span class="line">import org.springframework.test.context.junit4.SpringRunner;</span><br><span class="line">import org.springframework.test.util.ReflectionTestUtils;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.List;</span><br><span class="line"></span><br><span class="line">import static org.junit.jupiter.api.Assertions.assertEquals;</span><br><span class="line">import static org.mockito.Mockito.when;</span><br><span class="line"></span><br><span class="line">@SpringBootTest(classes = HellospringbootApplication.class,</span><br><span class="line">        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)</span><br><span class="line">@RunWith(SpringRunner.class)</span><br><span class="line">class HellospringbootApplicationTests &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    @Spy</span><br><span class="line">    private UserRegistrationRestController userRegistrationRestController;</span><br><span class="line"></span><br><span class="line">    @Mock</span><br><span class="line">    private UserJpaRepository userJpaRepository;</span><br><span class="line"></span><br><span class="line">    @BeforeEach</span><br><span class="line">    public void setup() &#123;</span><br><span class="line">        System.out.println(&quot;before被调用&quot;);</span><br><span class="line">        userRegistrationRestController = new UserRegistrationRestController();</span><br><span class="line">        ReflectionTestUtils.setField(userRegistrationRestController, &quot;userJpaRepository&quot;,userJpaRepository);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Test</span><br><span class="line">    public void testListAllUsers() &#123;</span><br><span class="line"></span><br><span class="line">        List&lt;UserDTO&gt; userList = new ArrayList&lt;UserDTO&gt;();</span><br><span class="line">        userList.add(new UserDTO());</span><br><span class="line">        when(this.userJpaRepository.findAll()).thenReturn(userList);</span><br><span class="line">        ResponseEntity&lt;List&lt;UserDTO&gt;&gt; responseEntity = this.userRegistrationRestController.listAllUsers();</span><br><span class="line">        assertEquals(HttpStatus.OK, responseEntity.getStatusCode());</span><br><span class="line">        assertEquals(1, responseEntity.getBody().size());</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    @AfterEach</span><br><span class="line">    public void teardown() &#123;</span><br><span class="line">        userRegistrationRestController = null;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>**记住这里的junit包里的API没有用.要使用junit.jupiter.api.**下的API<br>这段代码意思是:<br>@Spy 标记一个间谍对象.然后就可以监控这个对象,使用<br><code>when(this.userJpaRepository.findAll()).thenReturn(userList);</code>意思是只要这个间谍对象使用findAll()方法那么就返回一个userList.<br>@Mock 标记一个模拟对象.然后使用<code>ReflectionTestUtils.setField(userRegistrationRestController, &quot;userJpaRepository&quot;,userJpaRepository);</code>这个方法把mock对象注入监控对象中,因为这个监控对象依赖这个mock对象.<br>后面就不需要多将了.</p>
<h2 id="使用Spring-MVC测试框架测试Web层"><a href="#使用Spring-MVC测试框架测试Web层" class="headerlink" title="使用Spring MVC测试框架测试Web层"></a>使用Spring MVC测试框架测试Web层</h2><p>使用@WebMvcTest注解,该注解可以与@RunWith（SpringRunner.class）结合使用以进行典型的Spring MVC测试，<br>并且可以在测试仅关注Spring MVC组件时使用。带有@WebMvcTest注释的测试类将自动配置MockMvc（包括对HtmlUnit WebClient和Selenium WebDriver的支持）。</p>
<p>通常，@ WebMvcTest与@MockBean结合使用以创建@Controller bean所需的任何协作者。要开始使用Spring MVC Test框架，<br>您需要熟悉MockMVC .然后爆料一个大坑<strong>这里结合用的话需要使用junit下的test和before那些注解.否侧报错</strong></p>
<h3 id="MockMVC"><a href="#MockMVC" class="headerlink" title="MockMVC"></a>MockMVC</h3><p>MockMvc类是Spring MVC测试框架的中心类。org.springframework.test。web.servlet.MockMvc类可用于为使用Spring MVC框架开发的应用程序编写测试。MockMvc类可用于执行HTTP请求</p>
<p>MockMvc可以完全模拟Spring基础构建.它被实现the org.springframework.test.web.servlet.MockMvcBuilder接口的类创建<br>MockMvcBuilder接口通过大量使用建造模式去构建Spring MVC Test框架</p>
<p>MockMvc类包含执行方法,这个方法可以通过一个相关路径去运行测试用例.之后,expect方法可以响应信息中去验证不同的组件.</p>
<p><strong>上代码:</strong></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">import com.zhang.myproject.dto.UserDTO;</span><br><span class="line">import com.zhang.myproject.repository.UserJpaRepository;</span><br><span class="line">import com.zhang.myproject.rest.UserRegistrationRestController;</span><br><span class="line">import org.junit.Before;</span><br><span class="line">import org.junit.Test;</span><br><span class="line">import org.junit.runner.RunWith;</span><br><span class="line">import org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line">import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;</span><br><span class="line">import org.springframework.boot.test.mock.mockito.MockBean;</span><br><span class="line">import org.springframework.http.MediaType;</span><br><span class="line">import org.springframework.test.context.ContextConfiguration;</span><br><span class="line">import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;</span><br><span class="line">import org.springframework.test.web.servlet.MockMvc;</span><br><span class="line"></span><br><span class="line">import java.nio.charset.Charset;</span><br><span class="line">import java.util.Optional;</span><br><span class="line"></span><br><span class="line">import static org.hamcrest.Matchers.is;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">import static org.mockito.Mockito.when;</span><br><span class="line">import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;</span><br><span class="line">import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;</span><br><span class="line">import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;</span><br><span class="line">import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;</span><br><span class="line">import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * create by zhangbo on 2019/11/6 0006</span><br><span class="line"> */</span><br><span class="line">@RunWith(SpringJUnit4ClassRunner.class)</span><br><span class="line">@WebMvcTest(controllers = UserRegistrationRestController.class)</span><br><span class="line">@ContextConfiguration(classes = HellospringbootApplication.class)</span><br><span class="line">public class UserRegistrationControllerTest &#123;</span><br><span class="line"></span><br><span class="line">    @Autowired</span><br><span class="line">    private MockMvc mockMvc;</span><br><span class="line">    @MockBean</span><br><span class="line">    private UserJpaRepository userJpaRepositoryMock;</span><br><span class="line">    private MediaType contentType;</span><br><span class="line">    private UserDTO user;</span><br><span class="line">    private Optional&lt;UserDTO&gt; optUser;</span><br><span class="line">    @Before</span><br><span class="line">    public void setup() &#123;</span><br><span class="line">        contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),</span><br><span class="line">                MediaType.APPLICATION_JSON.getSubtype(), Charset.forName(&quot;utf8&quot;));</span><br><span class="line">        user = new UserDTO();</span><br><span class="line">        user.setName(&quot;Ravi Kant Soni&quot;);</span><br><span class="line">        user.setAddress(&quot;JP Nagar; Bangalore; India&quot;);</span><br><span class="line">        user.setEmail(&quot;ravikantsoni.author@gmail.com&quot;);</span><br><span class="line">        optUser = Optional.of(this.user);</span><br><span class="line">    &#125;</span><br><span class="line">    @Test</span><br><span class="line">    public void shouldReturnSuccessMessage() throws Exception &#123;</span><br><span class="line">        when(this.userJpaRepositoryMock.findById(1L)).thenReturn(optUser);</span><br><span class="line">        this.mockMvc.perform(get(&quot;/api/user/1&quot;))</span><br><span class="line">                .andExpect(status().isOk())</span><br><span class="line">                .andExpect(content().contentType(contentType))</span><br><span class="line">                .andExpect(jsonPath(&quot;$.name&quot;, is(&quot;Ravi Kant Soni&quot;)))</span><br><span class="line">                .andExpect(jsonPath(&quot;$.address&quot;, is(&quot;JP Nagar; Bangalore; India&quot;)))</span><br><span class="line">                .andExpect(jsonPath(&quot;$.email&quot;, is(&quot;ravikantsoni.author@gmail.com&quot;)))</span><br><span class="line">                .andDo(print());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>各种注解解释:</strong></p>
<ul>
<li><p><code>@runwith,@WebMvcTest,@ContextConfiguration:</code> 使用<code>@ContextConfiguration</code>去寻找主配置类.<code>@WebMvcTest</code>这注解被用作测试Web层,因此Spring boot会实例化Web层,不是整个容器</p>
</li>
<li><p><code>MockMvc:</code> 直接可以用autowired注入,这注解结合<code>@WebMvcTest</code>会注入一个完整的配置好的MockMvc实例.<br>使用<code>MockMvc</code>可以发送模拟的http请求到控制层并且测试不在服务器运行的控制层是怎么样的行为.</p>
</li>
<li><p><code>@MockBean:</code> 被它标注的会创建一个模拟对象并且注入该对象到MockMvc控制层实例中</p>
</li>
<li><p><code>before</code>,<code>test:</code> 看前面解释;</p>
</li>
</ul>
<p>使用了MockMvcRequestBuilders创建了一个get访问.当然它还有post,put,delete等对应的http请求</p>
<p>调用mockMvc的perform方法去执行一个get请求<code>&quot;/api/user/&#123;id&#125;&quot;</code>,并且对响应如下内容执行断言:</p>
<ul>
<li><p>HTTP状态码是否200</p>
</li>
<li><p>contentType里的内容</p>
</li>
<li><p>用户的属性值.在<code>jsonPath</code>方法中使用<code>jsonPath</code>表达式去写对于响应体的断言.简单说jsonPath对于JSON和xPath对于XML是<br>一样的</p>
</li>
</ul>
<p>最后调用<code>andDo(print())</code>去获得以下控制层的输出:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">MockHttpServletRequest:</span><br><span class="line">      HTTP Method = GET</span><br><span class="line">      Request URI = /api/user/1</span><br><span class="line">       Parameters = &#123;&#125;</span><br><span class="line">          Headers = []</span><br><span class="line">             Body = &lt;no character encoding set&gt;</span><br><span class="line">    Session Attrs = &#123;&#125;</span><br><span class="line"></span><br><span class="line">Handler:</span><br><span class="line">             Type = com.zhang.myproject.rest.UserRegistrationRestController</span><br><span class="line">           Method = com.zhang.myproject.rest.UserRegistrationRestController#getUserById(Long)</span><br><span class="line">Async:</span><br><span class="line">    Async started = false</span><br><span class="line">     Async result = null</span><br><span class="line"></span><br><span class="line">Resolved Exception:</span><br><span class="line">             Type = null</span><br><span class="line"></span><br><span class="line">ModelAndView:</span><br><span class="line">        View name = null</span><br><span class="line">             View = null</span><br><span class="line">            Model = null</span><br><span class="line">FlashMap:</span><br><span class="line">       Attributes = null</span><br><span class="line"></span><br><span class="line">MockHttpServletResponse:</span><br><span class="line">           Status = 200</span><br><span class="line">    Error message = null</span><br><span class="line">          Headers = [Vary:&quot;Origin&quot;, &quot;Access-Control-Request-Method&quot;, &quot;Access-Control-Request-Headers&quot;, Content-Type:&quot;application/json;charset=UTF-8&quot;, X-Content-Type-Options:&quot;nosniff&quot;, X-XSS-Protection:&quot;1; mode=block&quot;, Cache-Control:&quot;no-cache, no-store, max-age=0, must-revalidate&quot;, Pragma:&quot;no-cache&quot;, Expires:&quot;0&quot;, X-Frame-Options:&quot;DENY&quot;]</span><br><span class="line">     Content type = application/json;charset=UTF-8</span><br><span class="line">             Body = &#123;&quot;id&quot;:null,&quot;name&quot;:&quot;Ravi Kant Soni&quot;,&quot;address&quot;:&quot;JP Nagar; Bangalore; India&quot;,&quot;email&quot;:&quot;ravikantsoni.author@gmail.com&quot;&#125;</span><br><span class="line">    Forwarded URL = null</span><br><span class="line">   Redirected URL = null</span><br><span class="line">          Cookies = []</span><br></pre></td></tr></table></figure>

<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>使用RestTemplate创建了REST客户端，并使用它来执行客户端操作，例如对资源进行GET，POST，PUT和DELETE。研究了不同的<br>RestTemplate方法和操作以使用REST API。使用RestTemplate实现了基本身份验证。然后开始使用<code>Spring Test</code>框架测试<br>RESTful服务.介绍了单元测试和集成测试。在JUnit4框架中看到了不同的批注和断言方法然后一个模拟框架和模拟对象。<br>最后，使用Test框架和Spring MVC Test框架测试了REST服务。</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://zhangbo4881820.gitee.io/myblog">brad 随风起舞</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://zhangbo4881820.gitee.io/myblog/posts/21667/">https://zhangbo4881820.gitee.io/myblog/posts/21667/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://zhangbo4881820.gitee.io/myblog" target="_blank">静待花开</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/myblog/tags/%E5%AE%9E%E6%88%98/">实战</a></div><div class="post_share"><div class="social-share" data-image="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/social-share.js/1.0.16/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdnjs.cloudflare.com/ajax/libs/social-share.js/1.0.16/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/myblog/posts/58891/" title="markdown操作指引"><div class="cover" style="background: var(--default-bg-color)"></div><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">markdown操作指引</div></div></a></div><div class="next-post pull-right"><a href="/myblog/posts/12008/" title="全栈开发(AngularJS)"><div class="cover" style="background: var(--default-bg-color)"></div><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">全栈开发(AngularJS)</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/myblog/posts/45226/" title="全栈开发(AngularJS原理)"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2019-11-01</div><div class="title">全栈开发(AngularJS原理)</div></div></a></div><div><a href="/myblog/posts/12008/" title="全栈开发(AngularJS)"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2019-11-05</div><div class="title">全栈开发(AngularJS)</div></div></a></div><div><a href="/myblog/posts/20230/" title="全栈开发(Spring Security)"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2019-11-04</div><div class="title">全栈开发(Spring Security)</div></div></a></div><div><a href="/myblog/posts/45057/" title="全栈开发(Spring Boot Actuator)"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2019-11-07</div><div class="title">全栈开发(Spring Boot Actuator)</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png" onerror="this.onerror=null;this.src='/myblog/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">brad 随风起舞</div><div class="author-info__description">有些事情需要一辈子的沉淀</div></div><div class="card-info-data site-data is-center"><a href="/myblog/archives/"><div class="headline">文章</div><div class="length-num">57</div></a><a href="/myblog/tags/"><div class="headline">标签</div><div class="length-num">20</div></a><a href="/myblog/categories/"><div class="headline">分类</div><div class="length-num">25</div></a></div><a id="card-info-btn" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/xxxxx" target="_blank" title="Github"><i class="fab fa-github" style="color: #24292e;"></i></a><a class="social-icon" href="mailto:xxxxxx@gmail.com" target="_blank" title="Email"><i class="fas fa-envelope" style="color: #4a7dbe;"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">This is my Blog</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8RestTemplate%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AAREST%E5%AE%A2%E6%88%B7%E7%AB%AF"><span class="toc-number">1.</span> <span class="toc-text">使用RestTemplate构建一个REST客户端</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#RestTemplate"><span class="toc-number">1.1.</span> <span class="toc-text">RestTemplate</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#RestTemplate-%E6%96%B9%E6%B3%95"><span class="toc-number">1.2.</span> <span class="toc-text">RestTemplate 方法</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%B8%A6%E5%8F%82%E6%95%B0%E7%9A%84GET%E8%AF%B7%E6%B1%82%E5%8E%BB%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7"><span class="toc-number">2.</span> <span class="toc-text">带参数的GET请求去获取用户</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%B8%A6JSON%E6%95%B0%E6%8D%AE%E7%9A%84POST%E8%AE%BF%E9%97%AE%E5%88%9B%E5%BB%BA%E7%94%A8%E6%88%B7"><span class="toc-number">3.</span> <span class="toc-text">带JSON数据的POST访问创建用户</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%B8%A6%E5%8F%82%E6%95%B0%E7%9A%84put%E8%AF%B7%E6%B1%82%E6%9B%B4%E6%94%B9%E7%94%A8%E6%88%B7"><span class="toc-number">4.</span> <span class="toc-text">带参数的put请求更改用户</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%B8%A6%E5%8F%82%E6%95%B0%E7%9A%84delete%E8%AE%BF%E9%97%AE%E5%88%A0%E9%99%A4%E7%94%A8%E6%88%B7"><span class="toc-number">5.</span> <span class="toc-text">带参数的delete访问删除用户</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#RestTemplate-%E7%9A%84-Exchange-API"><span class="toc-number">6.</span> <span class="toc-text">RestTemplate 的 Exchange API</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#RestTemplate%E7%9A%84%E5%9F%BA%E6%9C%AC%E8%AE%A4%E8%AF%81"><span class="toc-number">7.</span> <span class="toc-text">RestTemplate的基本认证</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8Spring%E7%9A%84%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6%E6%B5%8B%E8%AF%95RESTful%E6%9C%8D%E5%8A%A1"><span class="toc-number">8.</span> <span class="toc-text">使用Spring的测试框架测试RESTful服务</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#Testing-Using-JUnit4"><span class="toc-number">8.1.</span> <span class="toc-text">Testing Using JUnit4</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B3%A8%E8%A7%A3%E6%B8%85%E5%8D%95"><span class="toc-number">8.1.1.</span> <span class="toc-text">注解清单</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Assert%E6%96%B9%E6%B3%95"><span class="toc-number">8.1.2.</span> <span class="toc-text">Assert方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Assert%E6%96%B9%E6%B3%95%E6%B8%85%E5%8D%95"><span class="toc-number">8.1.3.</span> <span class="toc-text">Assert方法清单</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0Junit%E6%B5%8B%E8%AF%95"><span class="toc-number">8.2.</span> <span class="toc-text">代码实现Junit测试</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Agile-Software-Testing-%E6%95%8F%E6%8D%B7%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95"><span class="toc-number">9.</span> <span class="toc-text">Agile Software Testing(敏捷软件测试)</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95"><span class="toc-number">9.1.</span> <span class="toc-text">单元测试</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E7%94%A8Mocking%E5%AF%B9%E8%B1%A1%E5%A4%84%E7%90%86%E4%BE%9D%E8%B5%96%E7%B1%BB"><span class="toc-number">9.1.1.</span> <span class="toc-text">单元测试用Mocking对象处理依赖类</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Mockito%E6%A1%86%E6%9E%B6"><span class="toc-number">9.1.1.1.</span> <span class="toc-text">Mockito框架</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%9C%A8Spring-boot%E5%BA%94%E7%94%A8%E4%B8%AD%E6%B5%8B%E8%AF%95"><span class="toc-number">10.</span> <span class="toc-text">在Spring boot应用中测试</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#Maven%E4%BE%9D%E8%B5%96"><span class="toc-number">10.1.</span> <span class="toc-text">Maven依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AE%9E%E7%8E%B0%E4%BB%A3%E7%A0%81"><span class="toc-number">10.2.</span> <span class="toc-text">实现代码</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%B5%8B%E8%AF%95REST-API"><span class="toc-number">10.3.</span> <span class="toc-text">测试REST API</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8Spring-MVC%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6%E6%B5%8B%E8%AF%95Web%E5%B1%82"><span class="toc-number">10.4.</span> <span class="toc-text">使用Spring MVC测试框架测试Web层</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#MockMVC"><span class="toc-number">10.4.1.</span> <span class="toc-text">MockMVC</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E6%80%BB%E7%BB%93"><span class="toc-number">11.</span> <span class="toc-text">总结</span></a></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/myblog/posts/15695/" title="jenkins（1）">jenkins（1）</a><time datetime="2023-09-14T02:17:44.000Z" title="发表于 2023-09-14 10:17:44">2023-09-14</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/myblog/posts/22012/" title="wsl的Ubuntu子系统（1）">wsl的Ubuntu子系统（1）</a><time datetime="2023-09-13T08:42:19.000Z" title="发表于 2023-09-13 16:42:19">2023-09-13</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/myblog/posts/30302/" title="effective-java-(接口优于抽象类)">effective-java-(接口优于抽象类)</a><time datetime="2023-09-12T08:12:48.000Z" title="发表于 2023-09-12 16:12:48">2023-09-12</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/myblog/posts/9473/" title="effective-java(创建和销毁对象--组合的形式优于继承)">effective-java(创建和销毁对象--组合的形式优于继承)</a><time datetime="2023-09-11T08:23:19.000Z" title="发表于 2023-09-11 16:23:19">2023-09-11</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/myblog/posts/16107/" title="Hello World">Hello World</a><time datetime="2023-09-07T09:44:01.910Z" title="发表于 2023-09-07 17:44:01">2023-09-07</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By brad 随风起舞</div><div class="framework-info"><span>框架 </span><a href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/myblog/js/utils.js"></script><script src="/myblog/js/main.js"></script><script src="/myblog/js/fancybox.umd.js"></script><div class="js-pjax"></div><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div class="no-result" id="local-search-results"></div><div id="local-search-stats-wrap"></div></div></div><div id="search-mask"></div><script src="/myblog/js/search/local-search.js"></script></div></div></body></html>